
     
var DIFFUSEMAP_SLOT         =   1,
    LIGHTMAP_SLOT           =   DIFFUSEMAP_SLOT + 1,
    //
    GLOBAL_SHADOWMAP_SLOT   =   LIGHTMAP_SLOT + 1,
    REFLECTIONMAP_SLOT      =   GLOBAL_SHADOWMAP_SLOT + 1,
    SCR_CLEAN_SLOT          =   REFLECTIONMAP_SLOT + 1,
    SCR_VBLUR_SLOT          =   SCR_CLEAN_SLOT + 1,
    SCR_BLUR_SLOT           =   SCR_VBLUR_SLOT + 1,
    SCR_GLOWED_SLOT         =   SCR_BLUR_SLOT + 1;
    
var g_num_slots = 8;

var SHADOW_PASS             =   0,
    REFLECTION_PASS         =   SHADOW_PASS + 1,
    BEAUTY_PASS             =   REFLECTION_PASS + 1,
    UI_DRAW_PASS            =   BEAUTY_PASS + 1;
    

var g_render_state = [];
    g_render_state.current_program = [];
    g_render_state.current_cam = [];
    g_render_state.cam_before_pass = [];
    g_render_state.current_pass = [];

var Renderer = (function(){
    //"use strict";
    function Renderer(file_name){
        
        var textures_ = [];
        var binded_textures_ = [];
        
        for(var i = 0; i < g_num_slots; i++)
        {
            binded_textures_.push(-1);
        }
        
        var global_shadow_map_ = [],
            reflection_map_ = [],
        
            local_shadow_map_cfg_ = [],
            local_shadow_maps_ = [],
        
            screen_draw_cfg_ = [],
        
            DrawFunc_,
        
        
            carpaint_program_,
            shadow_program_,
            screen_draw_program_,
            screen_draw_fxaa_program_,
            screen_draw_vblur_program_,
            screen_draw_hblur_program_,
            screen_draw_glow_program_,
            screen_text_program_,
        
            lines = GetLines(file_name),
        
            fps_counter_ = [];
        
        var line = 0;
        var val;
        while(line < lines.length)
        {
            if(lines[line].indexOf("GlobalShadowMap") != -1)
            {
                while(lines[line].indexOf("{") == -1)
                {
                    line++;
                }

                while(lines[line].indexOf("}") == -1)
                {
                    if(lines[line].indexOf("enabled") != -1)
                    {
                        val = lines[line].match(/\w+/g);
                        if(val[1] == "true")
                        {
                            global_shadow_map_.enabled = true;
                        } else
                        {
                            global_shadow_map_.enabled = false;
                        }
                    }else
                    if(lines[line].indexOf("cascaded") != -1)
                    {
                        val = lines[line].match(/\w+/g);
                        if(val[1] == "true")
                        {
                            global_shadow_map_.type = 1;
                        } else
                        {
                            global_shadow_map_.type = 0;
                        }
                        
                    }else
                    if(lines[line].indexOf("resolution") != -1)
                    {
                        val = lines[line].match(/\w+/g);
                        global_shadow_map_.resolution = parseInt(val[1], 10);
                        
                    }else
                    if(lines[line].indexOf("offset") != -1)
                    {
                        val = lines[line].match(/\w+/g);
                        global_shadow_map_.offset = parseFloat(val[1]);
                        
                        global_shadow_map_.cam_move = [global_shadow_map_.offset * sun_vec[0], global_shadow_map_.offset * sun_vec[1], global_shadow_map_.offset * sun_vec[2]];
                        
                    }
                    line++;
                }
            }else
            if(lines[line].indexOf("LocalShadowMaps") != -1)
            {
                while(lines[line].indexOf("{") == -1)
                {
                    line++;
                }

                //var val;

                while(lines[line].indexOf("}") == -1)
                {
                    if(lines[line].indexOf("enabled") != -1)
                    {
                        val = lines[line].match(/\w+/g);
                        if(val == "true")
                        {
                            local_shadow_map_cfg_.enabled = true;
                        } else
                        {
                            local_shadow_map_cfg_.enabled = false;
                        }
                    }else
                    if(lines[line].indexOf("resolution") != -1)
                    {
                        val = lines[line].match(/\w+/g);
                        local_shadow_map_cfg_.resolution = parseInt(val[1], 10);
                    }
                    line++;
                }
            }else
            if(lines[line].indexOf("ReflectionMap") != -1)
            {
                while(lines[line].indexOf("{") == -1)
                {
                    line++;
                }
                while(lines[line].indexOf("}") == -1)
                {
                    if(lines[line].indexOf("enabled") != -1)
                    {
                        val = lines[line].match(/\w+/g);
                        if(val[1] == "true")
                        {
                            reflection_map_.enabled = true;
                        } else
                        {
                            reflection_map_.enabled = false;
                        }
                    }else
                    if(lines[line].indexOf("resolution") != -1)
                    {
                        val = lines[line].match(/\w+/g);
                        reflection_map_.resolution = parseInt(val[1], 10);
                    }
                    line++;
                }
            }else
            if(lines[line].indexOf("ScreenDraw") != -1)
            {
                while(lines[line].indexOf("{") == -1)
                {
                    line++;
                }
                while(lines[line].indexOf("}") == -1)
                {
                    if(lines[line].indexOf("enable_fxaa") != -1)
                    {
                        val = lines[line].match(/\w+/g);
                        if(val[1] == "true")
                        {
                            screen_draw_cfg_.enable_fxaa = true;
                        } else
                        {
                            screen_draw_cfg_.enable_fxaa = false;
                        }
                    }else
                    if(lines[line].indexOf("enable_glow") != -1)
                    {
                        val = lines[line].match(/\w+/g);
                        if(val[1] == "true")
                        {
                            screen_draw_cfg_.enable_glow = true;
                        } else
                        {
                            screen_draw_cfg_.enable_glow = false;
                        }
                    }else
                    if(lines[line].indexOf("enable_vignette") != -1)
                    {
                        val = lines[line].match(/\w+/g);
                        if(val[1] == "true")
                        {
                            screen_draw_cfg_.enable_vignette = true;
                        } else
                        {
                            screen_draw_cfg_.enable_vignette = false;
                        }
                    }else
                    if(lines[line].indexOf("draw_fps") != -1)
                    {
                        val = lines[line].match(/\w+/g);
                        if(val[1] == "true")
                        {
                            screen_draw_cfg_.draw_fps = true;
                        } else
                        {
                            screen_draw_cfg_.draw_fps = false;
                        }
                    }else
                    if(lines[line].indexOf("draw_speed") != -1)
                    {
                        val = lines[line].match(/\w+/g);
                        if(val[1] == "true")
                        {
                            screen_draw_cfg_.draw_speed = true;
                        } else
                        {
                            screen_draw_cfg_.draw_speed = false;
                        }
                    }
                    line++;
                }
            }
            line++;
        }
        
        lines = null;

        var extensions_ = [];
            extensions_.texture_float = gl.getExtension("OES_texture_float");
            extensions_.texture_float_linear = gl.getExtension("OES_texture_float_linear");

            extensions_.texture_anisotropy = gl.getExtension("MOZ_EXT_texture_filter_anisotropic");
            if(extensions_.texture_anisotropy === null)
            {
                extensions_.texture_anisotropy = gl.getExtension("WEBKIT_EXT_texture_filter_anisotropic");
            }
            if(extensions_.texture_anisotropy !== null)
            {
                extensions_.max_anisotropy = gl.getParameter(extensions_.texture_anisotropy.MAX_TEXTURE_MAX_ANISOTROPY_EXT);
            }


        if(global_shadow_map_.enabled)
        {
            //global_shadow_map_.total_cam = new Camera([0.0, 0.0, 0.0], [0.0, 0.0, 0.0]);
            global_shadow_map_.cam = new Camera([0.0, 0.0, 0.0], [0.0, 0.0, 0.0]);
            global_shadow_map_.cam.Orthographic(-256*5*27, 256*5*27, -256*5*27, 256*5*27, 10.0, 1000000.0);
        
            global_shadow_map_.fbo = gl.createFramebuffer();
            gl.bindFramebuffer(gl.FRAMEBUFFER, global_shadow_map_.fbo);
        
            global_shadow_map_.texture = gl.createTexture();
            gl.bindTexture(gl.TEXTURE_2D, global_shadow_map_.texture);

            if(extensions_.texture_float === null)
            {
                gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, global_shadow_map_.resolution, global_shadow_map_.resolution, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
            } else
            {
                gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, global_shadow_map_.resolution, global_shadow_map_.resolution, 0, gl.RGBA, gl.FLOAT, null);
            }
        
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
        
            gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, global_shadow_map_.texture, 0);
        
            global_shadow_map_.rbo = gl.createRenderbuffer();
            gl.bindRenderbuffer(gl.RENDERBUFFER, global_shadow_map_.rbo);

            gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, global_shadow_map_.resolution, global_shadow_map_.resolution);
        
            gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, global_shadow_map_.rbo);
        
            gl.bindFramebuffer(gl.FRAMEBUFFER, null);
            
            gl.activeTexture(gl.TEXTURE0 + GLOBAL_SHADOWMAP_SLOT);
            gl.bindTexture(gl.TEXTURE_2D, global_shadow_map_.texture);
            gl.activeTexture(gl.TEXTURE0);
        }
        
        if(reflection_map_.enabled)
        {
            reflection_map_.cam = new Camera([0.0, 100.0, 0.0], [0.0, 0.0, 0.0]);
            reflection_map_.cam.Perspective(90, 1.0, 0.5, 10000.0);
                
            reflection_map_.fbo = gl.createFramebuffer();
            gl.bindFramebuffer(gl.FRAMEBUFFER, reflection_map_.fbo);
        
            reflection_map_.texture = gl.createTexture();
            gl.bindTexture(gl.TEXTURE_CUBE_MAP, reflection_map_.texture);
                    
            gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
            gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
            gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
            gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
            
            //var i;
            for (i = 0; i < 6; i++)
            {
                gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, reflection_map_.resolution, reflection_map_.resolution, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
            }
            
                    
            reflection_map_.rbo = gl.createRenderbuffer();
            gl.bindRenderbuffer(gl.RENDERBUFFER, reflection_map_.rbo);
        
            reflection_map_.depth_texture = gl.createTexture();
            gl.bindTexture(gl.TEXTURE_CUBE_MAP, reflection_map_.depth_texture);
                
            for (i = 0; i < 6; i++)
            {
                gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, reflection_map_.resolution, reflection_map_.resolution, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
            }

            gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
            gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
            gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
            gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
            
            gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, reflection_map_.resolution, reflection_map_.resolution);
            
            gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, reflection_map_.rbo);
        
            gl.bindFramebuffer(gl.FRAMEBUFFER, null);
        
            gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
            
                
            gl.activeTexture(gl.TEXTURE0+REFLECTIONMAP_SLOT);
            gl.bindTexture(gl.TEXTURE_2D, null);
            gl.bindTexture(gl.TEXTURE_CUBE_MAP, reflection_map_.texture);
            gl.activeTexture(gl.TEXTURE0);
        }

        if((screen_draw_cfg_.enable_fxaa) || (screen_draw_cfg_.enable_glow) || (screen_draw_cfg_.enable_vignette))
        {
            screen_draw_cfg_.screen_quad = new FullScreenQuad();
            screen_draw_cfg_.scr_clean_fbo = gl.createFramebuffer();
            gl.bindFramebuffer(gl.FRAMEBUFFER, screen_draw_cfg_.scr_clean_fbo);
            
            screen_draw_cfg_.scr_clean = gl.createTexture();
            gl.activeTexture(gl.TEXTURE0+SCR_CLEAN_SLOT);
            gl.bindTexture(gl.TEXTURE_2D, screen_draw_cfg_.scr_clean);
            
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, g_canvas.width, g_canvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
        
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

            gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, screen_draw_cfg_.scr_clean, 0);
        
                                
            screen_draw_cfg_.rbo = gl.createRenderbuffer();
            gl.bindRenderbuffer(gl.RENDERBUFFER, screen_draw_cfg_.rbo);

            gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, g_canvas.width, g_canvas.height);
        
            gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, screen_draw_cfg_.rbo);
        

            /********************************************************/

            if(screen_draw_cfg_.enable_glow)
            {
                screen_draw_cfg_.scr_vblur_fbo = gl.createFramebuffer();
                gl.bindFramebuffer(gl.FRAMEBUFFER, screen_draw_cfg_.scr_vblur_fbo);
                
                screen_draw_cfg_.scr_vblur = gl.createTexture();
                gl.activeTexture(gl.TEXTURE0+SCR_VBLUR_SLOT);
                gl.bindTexture(gl.TEXTURE_2D, screen_draw_cfg_.scr_vblur);
            
                gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, g_canvas.width, g_canvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
            
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
        
                gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, screen_draw_cfg_.scr_vblur, 0);
                
                
                //
                
                screen_draw_cfg_.scr_blur_fbo = gl.createFramebuffer();
                gl.bindFramebuffer(gl.FRAMEBUFFER, screen_draw_cfg_.scr_blur_fbo);
                
                screen_draw_cfg_.scr_blur = gl.createTexture();
                gl.activeTexture(gl.TEXTURE0+SCR_BLUR_SLOT);
                gl.bindTexture(gl.TEXTURE_2D, screen_draw_cfg_.scr_blur);
                
        
                gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, g_canvas.width, g_canvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
                
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
        
                gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, screen_draw_cfg_.scr_blur, 0);
                
                gl.activeTexture(gl.TEXTURE0);
                
                //
                
                                
                screen_draw_cfg_.scr_glowed_fbo = gl.createFramebuffer();
                gl.bindFramebuffer(gl.FRAMEBUFFER, screen_draw_cfg_.scr_glowed_fbo);
                
                gl.activeTexture(gl.TEXTURE0+SCR_GLOWED_SLOT);
                screen_draw_cfg_.scr_glowed = gl.createTexture();
                gl.bindTexture(gl.TEXTURE_2D, screen_draw_cfg_.scr_glowed);
        
                gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, g_canvas.width, g_canvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
                
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
        
                gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, screen_draw_cfg_.scr_glowed, 0);
                                
                gl.activeTexture(gl.TEXTURE0);
        
            }
            
            gl.bindFramebuffer(gl.FRAMEBUFFER, null);
            gl.activeTexture(gl.TEXTURE0);

            if (screen_draw_cfg_.draw_fps) {
                
                fps_counter_.frame = new WinFrame(0.75, 0.75, 0.2, 0.2, 0, null, 0);
                fps_counter_.text = new TypeMesh("FPS = ", 0.795, 0.813, 1, null);
                fps_counter_.Update = function(fps){
                    fps_counter_.text.ChangeText("FPS = " + fps);
                };
                fps_counter_.Draw = function(){
                    fps_counter_.frame.Draw();
                    fps_counter_.text.Draw();
                };
                fps_counter_.frame.listen_move = false;
                fps_counter_.frame.OnClick = function(){
                    this.listen_move = true;
                };
                fps_counter_.frame.OnMove = function(dx, dy){
                    fps_counter_.frame.Move(dx, dy);
                    fps_counter_.text.Move(dx, dy);
                };
                fps_counter_.frame.OnUp = function(){
                    this.listen_move = false;
                };

                fps_counter_.fps_time = 0;
                fps_counter_.fps = 0;
                fps_counter_.last_fps_upfate = 0;
                fps_counter_.draw_time = 0;

                AddButton(fps_counter_.frame);
            }
            /********************************************************/
            
            
        }
        
        this.set_draw_func = function(func){
            DrawFunc_ = func;
        };
        
        this.global_shadow_cam = function(){
            return global_shadow_map_.cam;
        };
        
        this.DrawScene = function(){
            
            
            if(global_shadow_map_.enabled)
            {
                g_render_state.current_pass = SHADOW_PASS;
                
                gl.useProgram(shadow_program_);
                g_render_state.current_program = shadow_program_;
                
                cam_before = g_render_state.current_cam;
                
                g_render_state.current_cam = global_shadow_map_.cam;
                
                //this.SetupCurrentProgram();
                
                gl.bindFramebuffer(gl.FRAMEBUFFER, global_shadow_map_.fbo);
                
                
                if(global_shadow_map_.type == 1)
                {
                    var frontx, fronty, frontz,
                        pos, map_vertical_fac, map_horizontal_fac,
                        proj_x, proj_y;
                    for(var cam_index = 0; cam_index < 4; cam_index++)
                    {
                        switch(cam_index){
                            case 0:
                                gl.clearColor(0.0, 0.0, 0.0, 1.0);
                                gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
                
                                frontx = cam_before.view_matrix()[2];
                                fronty = cam_before.view_matrix()[6];
                                frontz = cam_before.view_matrix()[10];

                                pos = cam_before.world_position();

                                g_render_state.current_cam.LookAtMatrix([pos[0] - frontx*256*5*27 + global_shadow_map_.cam_move[0], pos[1] - fronty*256*5*27 + global_shadow_map_.cam_move[1], pos[2] - frontz*256*5*27 + global_shadow_map_.cam_move[2]], [pos[0] - frontx*256*5*27, pos[1] - fronty*256*5*27, pos[2] - frontz*256*5*27], [-frontx, -fronty, -frontz]);
                
                                map_vertical_fac = Math.abs(frontx * g_render_state.current_cam.view_matrix()[1] + fronty * g_render_state.current_cam.view_matrix()[5] + frontz * g_render_state.current_cam.view_matrix()[9]);
                
                                var proj_x = global_device_aspect * Math.abs(cam_before.view_matrix()[0] * g_render_state.current_cam.view_matrix()[0] +cam_before.view_matrix()[4] * g_render_state.current_cam.view_matrix()[4] + cam_before.view_matrix()[8] * g_render_state.current_cam.view_matrix()[8]);
                                var proj_y = Math.abs(cam_before.view_matrix()[1] * g_render_state.current_cam.view_matrix()[0] +cam_before.view_matrix()[5] * g_render_state.current_cam.view_matrix()[4] + cam_before.view_matrix()[9] * g_render_state.current_cam.view_matrix()[8]);
                
                                var inv_max = 1.0/Math.max(proj_x, proj_y);

                                map_horizontal_fac = inv_max;

                                g_render_state.current_cam.Orthographic(-256*5*map_horizontal_fac, 256*5*map_horizontal_fac, -256*5*27*map_vertical_fac, -256*5*25*map_vertical_fac, 1.0, 10000000.0);
                
                                //global_shadow_map_.cam.LookAtMatrix([-pos[0] - frontx*256*5*27 + global_shadow_map_.cam_move[0], -pos[1] - fronty*256*5*27 + global_shadow_map_.cam_move[1], -pos[2] - frontz*256*5*27 + global_shadow_map_.cam_move[2]], [-pos[0] - frontx*256*5*27, -pos[1] - fronty*256*5*27, -pos[2] - frontz*256*5*27], [-frontx, -fronty, -frontz]);
                            
                                //global_shadow_map_.cam.Orthographic(-256*5*27*map_horizontal_fac, 256*5*27*map_horizontal_fac, -256*5*27*map_vertical_fac, 256*5*27*map_vertical_fac, 1.0, 10000000.0);
                
                                gl.viewport(0, 0, global_shadow_map_.resolution/2, global_shadow_map_.resolution/2);
                            break;
                            case 1:
                                g_render_state.current_cam.Orthographic(-256*5*3*map_horizontal_fac, 256*5*3*map_horizontal_fac, -256*5*25*map_vertical_fac, -256*5*21*map_vertical_fac, 1.0, 10000000.0);
                
                                gl.viewport(global_shadow_map_.resolution/2 - 1, 0, global_shadow_map_.resolution/2, global_shadow_map_.resolution/2);
                            break;
                            case 2:
                                g_render_state.current_cam.Orthographic(-256*5*9*map_horizontal_fac, 256*5*9*map_horizontal_fac, -256*5*21*map_vertical_fac, -256*5*9*map_vertical_fac, 1.0, 10000000.0);
                                
                                gl.viewport(0, global_shadow_map_.resolution/2 - 1, global_shadow_map_.resolution/2, global_shadow_map_.resolution/2);
                            break;
                            case 3:
                                g_render_state.current_cam.Orthographic(-256*5*27*map_horizontal_fac, 256*5*27*map_horizontal_fac, -256*5*9*map_vertical_fac, 256*5*27*map_vertical_fac, 1.0, 10000000.0);
                
                                gl.viewport(global_shadow_map_.resolution/2 - 1, global_shadow_map_.resolution/2 - 1, global_shadow_map_.resolution/2, global_shadow_map_.resolution/2);
                            break;
                        }
                        
                        this.SetupCurrentProgram();
                        
                        /****************************************************/
                        
                        DrawFunc_();
                        
                        /****************************************************/
                    }
                    
                    //current_cam.LookAtMatrix([-pos[0] - frontx*256*5*27 + global_shadow_map_.cam_move[0], -pos[1] - fronty*256*5*27 + global_shadow_map_.cam_move[1], -pos[2] - frontz*256*5*27 + global_shadow_map_.cam_move[2]], [-pos[0] - frontx*256*5*27, -pos[1] - fronty*256*5*27, -pos[2] - frontz*256*5*27], [-frontx, -fronty, -frontz]);
                    g_render_state.current_cam.Orthographic(-256*5*27*map_horizontal_fac, 256*5*27*map_horizontal_fac, -256*5*27*map_vertical_fac, 256*5*27*map_vertical_fac, 1.0, 10000000.0);
                }
                
                g_render_state.current_cam = cam_before;
                
            }
            
            
            if(reflection_map_.enabled)
            {
                g_render_state.current_pass = REFLECTION_PASS;
                
                gl.useProgram(carpaint_program_);
                g_render_state.current_program = carpaint_program_;
                
                this.SetupCurrentProgram();
            
                gl.bindFramebuffer(gl.FRAMEBUFFER, reflection_map_.fbo);
        
                cam_before = g_render_state.current_cam;
                g_render_state.current_cam = reflection_map_.cam;

                gl.viewport(0, 0, reflection_map_.resolution, reflection_map_.resolution);
                gl.clearColor(0.48, 0.55, 0.63, 1.0);
                
                var pos = [0.0, 0.0, 0.0];

                pos[0] = -g_player.position()[0];
                pos[1] = -g_player.position()[1]-75.0;
                pos[2] = -g_player.position()[2];

                for(var face = 0; face < 6; face++){
                    
                    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + face, reflection_map_.texture, 0);
                    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
                    
                                
                    switch(face){
                        case 0:
                            reflection_map_.cam.LookAtMatrix([0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, -1.0, 0.0]);
                            break;
                        case 1:
                            reflection_map_.cam.LookAtMatrix([0.0, 0.0, 0.0], [-1.0, 0.0, 0.0], [0.0, -1.0, 0.0]);
                            break;
                        case 2:
                            reflection_map_.cam.LookAtMatrix([0.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]);
                            break;
                        case 3:
                            reflection_map_.cam.LookAtMatrix([0.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, -1.0]);
                            break;
                        case 4:
                            reflection_map_.cam.LookAtMatrix([0.0, 0.0, 0.0], [0.0, 0.0, 1.0], [0.0, -1.0, 0.0]);
                            break;
                        case 5:
                            reflection_map_.cam.LookAtMatrix([0.0, 0.0, 0.0], [0.0, 0.0, -1.0], [0.0, -1.0, 0.0]);
                            break;
                        default:
                            break;
                    }
                    
                    reflection_map_.cam.Translate(pos[0], pos[1], pos[2]);
                    
                    /****************************************************/
                    
                    DrawFunc_();
                    
                    /****************************************************/
            
                }
                
                g_render_state.current_cam = cam_before;
                
            }
            
            /* Ãëàâíûé ïðîõîä */
            
            g_render_state.current_pass = BEAUTY_PASS;
            
            if((screen_draw_cfg_.enable_fxaa) || (screen_draw_cfg_.enable_glow) || (screen_draw_cfg_.enable_vignette))
            {
                gl.bindFramebuffer(gl.FRAMEBUFFER, screen_draw_cfg_.scr_clean_fbo);
            }else
            {
                gl.bindFramebuffer(gl.FRAMEBUFFER, null);
            }
            
            gl.viewport(0, 0, g_canvas.width, g_canvas.height);
            gl.clearColor(0.48, 0.55, 0.63, 1.0);
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
            
            gl.useProgram(carpaint_program_);
            g_render_state.current_program = carpaint_program_;
            
            //current_cam = global_shadow_map_.cam;
            
            this.SetupCurrentProgram();
            
            /****************************************************/
            
            DrawFunc_();
            
            /****************************************************/
            
            //current_cam = front_cam;
                            
            /* Ïîñò */
            
            if(screen_draw_cfg_.enable_glow)
            {
                
                /*Ðàçìûòèå ïî âåðòèêàëè*/

                gl.bindFramebuffer(gl.FRAMEBUFFER, screen_draw_cfg_.scr_vblur_fbo);
                gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        
                gl.useProgram(screen_draw_vblur_program_);
                g_render_state.current_program = screen_draw_vblur_program_;
        
                gl.uniform1f(g_render_state.current_program.blur_size, 1/g_canvas.height);
        
                gl.uniform1i(g_render_state.current_program.s_texture, SCR_CLEAN_SLOT);
        
                screen_draw_cfg_.screen_quad.Draw();
                                                

                /*Ðàçìûòèå ïî ãîðèçîíòàëè*/
    
                gl.bindFramebuffer(gl.FRAMEBUFFER, screen_draw_cfg_.scr_blur_fbo);
                gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
                
                gl.useProgram(screen_draw_hblur_program_);
                g_render_state.current_program = screen_draw_hblur_program_;
        
                gl.uniform1f(g_render_state.current_program.blur_size, 1/g_canvas.width);
                
                gl.uniform1i(g_render_state.current_program.s_texture, SCR_VBLUR_SLOT);
        
                screen_draw_cfg_.screen_quad.Draw();
                
                
                /*Ñìåøèâàíèå*/
                
                if(screen_draw_cfg_.enable_fxaa)
                {
                    gl.bindFramebuffer(gl.FRAMEBUFFER, screen_draw_cfg_.scr_glowed_fbo);
                    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
                }else
                {
                    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
                }
    
                gl.useProgram(screen_draw_glow_program_);
                g_render_state.current_program = screen_draw_glow_program_;
        
                gl.uniform1i(g_render_state.current_program.s_texture, SCR_CLEAN_SLOT);

                gl.uniform1i(g_render_state.current_program.s_texture1, SCR_BLUR_SLOT);
                
                if(screen_draw_cfg_.enable_vignette)
                {
                    gl.uniform1i(g_render_state.current_program.vignette_enable, 1);
                }
        
                screen_draw_cfg_.screen_quad.Draw();
                
            }
            
            if(screen_draw_cfg_.enable_fxaa)
            {
                gl.bindFramebuffer(gl.FRAMEBUFFER, null);
                gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

                gl.useProgram(screen_draw_fxaa_program_);
                g_render_state.current_program = screen_draw_fxaa_program_;
                
                gl.uniform2f(g_render_state.current_program.texcoordOffset, 1/g_canvas.width, 1/g_canvas.height);
                
                if(screen_draw_cfg_.enable_glow)
                {
                    gl.uniform1i(g_render_state.current_program.s_texture, SCR_GLOWED_SLOT);
                } else
                {
                    gl.uniform1i(g_render_state.current_program.s_texture, SCR_CLEAN_SLOT);
                }
                
                screen_draw_cfg_.screen_quad.Draw();
            }
            
            gl.disable(gl.DEPTH_TEST);
            
            g_render_state.current_pass = UI_DRAW_PASS;
            
            gl.blendFunc (gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
            gl.enable(gl.BLEND);
            
            gl.useProgram(screen_draw_program_);
            g_render_state.current_program = screen_draw_program_;

            this.SetupCurrentProgram();
            

            if (screen_draw_cfg_.draw_fps) {
                if(fps_counter_.last_fps_upfate < (g_cur_time - 1000))
                {
                    fps_counter_.Update(fps_counter_.fps);
                    fps_counter_.last_fps_upfate = g_cur_time;
                    fps_counter_.fps = 0;
                }

                fps_counter_.Draw();
                fps_counter_.fps_time += g_delta_time;
                fps_counter_.fps++;
            }

            DrawGameUI();

            //g_start_hint.Draw();

            //g_transmission_ctrl.Draw();
            
            gl.enable(gl.DEPTH_TEST);
            gl.disable (gl.BLEND);
            
                        

            
        };
        
        this.SetupCurrentProgram = function(){

                if(g_render_state.current_program.hasOwnProperty("s_texture"))
                {
                    gl.uniform1i(g_render_state.current_program.s_texture, DIFFUSEMAP_SLOT);
                }
                
                if(g_render_state.current_program.hasOwnProperty("shadow_texture"))
                {
                    gl.uniform1i(g_render_state.current_program.shadow_texture, GLOBAL_SHADOWMAP_SLOT);
                }
                    
                if(g_render_state.current_program.hasOwnProperty("reflection_texture"))
                {
                    gl.uniform1i(g_render_state.current_program.reflection_texture, REFLECTIONMAP_SLOT);
                }
                    
                if(g_render_state.current_program.hasOwnProperty("sun_vector"))
                {
                    gl.uniform3f(g_render_state.current_program.sun_vector, sun_vec[0], sun_vec[1], sun_vec[2]);
                }
                
                if(g_render_state.current_program.hasOwnProperty("cube_map_pos"))
                {
                    gl.uniform3f(g_render_state.current_program.cube_map_pos, (g_render_state.current_cam.world_position()[0]), (g_render_state.current_cam.world_position()[1]-75.0), (g_render_state.current_cam.world_position()[2]));
                }
                
                if(global_shadow_map_.enabled)
                {
                    if(g_render_state.current_program.hasOwnProperty("uSunPMatrix"))
                    {
                        gl.uniformMatrix4fv(g_render_state.current_program.uSunPMatrix, false, global_shadow_map_.cam.view_proj_matrix());
                    }
                }
                
        };
        
        this.LoadTexture = function(file_name, repeat, filter){
            
            var new_texture = gl.createTexture();
            var new_image = new Image();
            new_texture.ready = false;
            //new_image.onload = function() { g_renderer.handleTextureLoaded(new_image, new_texture, repeat, filter); new_texture.ready = true;}
            
            new_image.onload = function(){
                gl.bindTexture(gl.TEXTURE_2D, new_texture);
                gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, new_image);
                if(!filter)
                {
                    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
                    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
                }else
                {
                    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
                    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
                }
  
                if(repeat)
                {
                    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
                    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
                }else
                {
                    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
                    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
                }
            
                if(!filter)
                {
                    gl.hint(gl.GENERATE_MIPMAP_HINT, gl.NICEST);
                    gl.generateMipmap(gl.TEXTURE_2D);
                }

                if(extensions_.texture_anisotropy !== null){
                    gl.texParameterf(gl.TEXTURE_2D, extensions_.texture_anisotropy.TEXTURE_MAX_ANISOTROPY_EXT, extensions_.max_anisotropy);
                }
                gl.bindTexture(gl.TEXTURE_2D, null);
            };

            new_image.src = file_name;
            

            textures_.push(new_texture);
            return (textures_.length - 1);
            
        };
        
        this.TexturesReady = function(){
            
            for(var i = 0; i < textures_.length; i++)
            {
                if(!textures_[i].ready)
                {
                    return false;
                }
            }
            
            return true;
            
        };
        
        this.UseProgram = function(prog){
            switch(prog){
                case 0:
                    g_render_state.current_program = carpaint_program;
                    break;
                case 1:
                    g_render_state.current_program = world_program;
                    break;
            }
        };
        
        this.BindTexture = function(slot, index){
            
            if(g_render_state.current_program != shadow_program_)
            {
                if(binded_textures_[slot] != index)
                {
                    gl.activeTexture(gl.TEXTURE0 + slot);
                    gl.bindTexture(gl.TEXTURE_2D, textures_[index]);
                    binded_textures_[slot] = index;
                }
            }
            
        };
            
        
        this.handleTextureLoaded = function(image, texture, repeat, filter) {
            gl.bindTexture(gl.TEXTURE_2D, texture);
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
            if(!filter)
            {
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
            }else
            {
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
            }
  
            if(repeat)
            {
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
            }else
            {
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
            }
            
            if(!filter)
            {
                gl.hint(gl.GENERATE_MIPMAP_HINT, gl.NICEST);
                gl.generateMipmap(gl.TEXTURE_2D);
            }
  
            var ext;
            ext = gl.getExtension("MOZ_EXT_texture_filter_anisotropic");
            if(ext === null)
            {
                ext = gl.getExtension("WEBKIT_EXT_texture_filter_anisotropic");
            }

            if(ext !== null){
                var max_anisotropy = gl.getParameter(ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT);
                gl.texParameterf(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy);
            }
            gl.bindTexture(gl.TEXTURE_2D, null);
            //texture.ready = true;
        };
        
        this.CreateAndLinkProgram = function(vs, fs){
            var str_vs = GetText(vs);
            var str_fs = GetText(fs);
            
            var vertex_shader = gl.createShader(gl.VERTEX_SHADER);
            
            gl.shaderSource(vertex_shader, str_vs);
            gl.compileShader(vertex_shader);
            
            if (!gl.getShaderParameter(vertex_shader, gl.COMPILE_STATUS)) {
                alert(gl.getShaderInfoLog(vertex_shader));
                return null;
            }
                
            var fragment_shader = gl.createShader(gl.FRAGMENT_SHADER);
            
            gl.shaderSource(fragment_shader, str_fs);
            gl.compileShader(fragment_shader);
            
            if (!gl.getShaderParameter(fragment_shader, gl.COMPILE_STATUS)) {
                alert(gl.getShaderInfoLog(fragment_shader));
                return null;
            }
            
                        
            var prog = gl.createProgram();
            gl.attachShader(prog, vertex_shader);
            gl.attachShader(prog, fragment_shader);
            gl.linkProgram(prog);

            if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
                alert("Could not initialise shaders");
            }
            
            var lines = str_vs.split("\r\n");
            var line = 0;
            
            while(line < lines.length)
            {
                if(lines[line].indexOf("attribute") != -1)
                {
                    var name = lines[line].match(/\w+/g);
                    prog[name[2]] = gl.getAttribLocation(prog, name[2]);
                }else
                if(lines[line].indexOf("uniform") != -1)
                {
                    var name = lines[line].match(/\w+/g);
                    prog[name[2]] = gl.getUniformLocation(prog, name[2]);
                }
                line++;
            }
            
            
            lines = str_fs.split("\r\n");
            line = 0;
            
            while(line < lines.length)
            {
                
                if(lines[line].indexOf("uniform") != -1)
                {
                    var name = lines[line].match(/\w+/g);
                    if(!prog.hasOwnProperty(name[2]))
                    {
                        prog[name[2]] = gl.getUniformLocation(prog, name[2]);
                    }
                }
                line++;
            }
            
            return prog;
        };
        
        
        this.InitShaders = function() {
            carpaint_program_ = this.CreateAndLinkProgram("shaders/car_paint.vs", "shaders/car_paint.fs");
            g_render_state.current_program = carpaint_program_;
            shadow_program_ = this.CreateAndLinkProgram("shaders/shadow.vs", "shaders/shadow.fs");
            screen_draw_program_ = this.CreateAndLinkProgram("shaders/screen_draw.vs", "shaders/screen_draw.fs");
            screen_draw_fxaa_program_ = this.CreateAndLinkProgram("shaders/screen_draw_fxaa.vs", "shaders/screen_draw_fxaa2.fs");
            screen_draw_vblur_program_ = this.CreateAndLinkProgram("shaders/screen_draw.vs", "shaders/screen_draw_gauss_vblur.fs");
            screen_draw_hblur_program_ = this.CreateAndLinkProgram("shaders/screen_draw.vs", "shaders/screen_draw_gauss_hblur.fs");
            screen_draw_glow_program_ = this.CreateAndLinkProgram("shaders/screen_draw.vs", "shaders/screen_draw_glow.fs");
            screen_text_program_ = this.CreateAndLinkProgram("shaders/screen_text.vs", "shaders/screen_text.fs");
            
            /*carpaint_program = this.CreateAndLinkProgram("shaders/car_paint.vs", "shaders/car_paint.fs");
            shadow_program = this.CreateAndLinkProgram("shaders/shadow.vs", "shaders/shadow.fs");
            screen_draw_program = this.CreateAndLinkProgram("shaders/screen_draw.vs", "shaders/screen_draw.fs");
            screen_draw_fxaa_program = this.CreateAndLinkProgram("shaders/screen_draw_fxaa.vs", "shaders/screen_draw_fxaa.fs");
            screen_draw_vblur_program = this.CreateAndLinkProgram("shaders/screen_draw.vs", "shaders/screen_draw_gauss_vblur.fs");
            screen_draw_hblur_program = this.CreateAndLinkProgram("shaders/screen_draw.vs", "shaders/screen_draw_gauss_hblur.fs");
            screen_draw_glow_program = this.CreateAndLinkProgram("shaders/screen_draw.vs", "shaders/screen_draw_glow.fs");
            screen_text_program = this.CreateAndLinkProgram("shaders/screen_text.vs", "shaders/screen_text.fs");*/
        };
        
        this.Resize = function(){
            
            if((screen_draw_cfg_.enable_fxaa) || (screen_draw_cfg_.enable_glow) || (screen_draw_cfg_.enable_vignette))
            {
                gl.activeTexture(gl.TEXTURE0+SCR_CLEAN_SLOT);
                gl.bindTexture(gl.TEXTURE_2D, screen_draw_cfg_.scr_clean);
            
                gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, g_canvas.width, g_canvas.height, 0, gl.RGBA, gl.FLOAT, null);
        
                gl.bindRenderbuffer(gl.RENDERBUFFER, screen_draw_cfg_.rbo);

                gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, g_canvas.width, g_canvas.height);


                if(screen_draw_cfg_.enable_glow)
                {

                    gl.activeTexture(gl.TEXTURE0+SCR_VBLUR_SLOT);
                    gl.bindTexture(gl.TEXTURE_2D, screen_draw_cfg_.scr_vblur);
            
                    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, g_canvas.width, g_canvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
            
                    //

                    gl.activeTexture(gl.TEXTURE0+SCR_BLUR_SLOT);
                    gl.bindTexture(gl.TEXTURE_2D, screen_draw_cfg_.scr_blur);
                
        
                    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, g_canvas.width, g_canvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
                
                    //
                
                    gl.activeTexture(gl.TEXTURE0+SCR_GLOWED_SLOT);
                    gl.bindTexture(gl.TEXTURE_2D, screen_draw_cfg_.scr_glowed);
        
                    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, g_canvas.width, g_canvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
            
                }
            
            gl.activeTexture(gl.TEXTURE0);
            
            }
            
        };
        

        
    }
    
    
    return Renderer;
})();


function IsInFrustrum(bbox_min, bbox_max){
    //
}